﻿using System;
using System.Diagnostics.Contracts;
using System.Runtime.InteropServices;

namespace O1.Data
{
    [ContractVerification(true)]
    [StructLayout(LayoutKind.Explicit)]
    public struct CellVersion : IEquatable<CellVersion>, IComparable<CellVersion>
    {
        public const int Size = Sz.Cell.CellVersion;

        [FieldOffset(0)]
        public readonly long BeginTransaction;

        [FieldOffset(Sz.CLR.Int64)]
        public readonly long EndTransaction;

        public CellVersion(long beginTransaction, long endTransaction)
            : this()
        {
            this.BeginTransaction = beginTransaction;
            this.EndTransaction = endTransaction;
        }

        public static bool operator ==(CellVersion instance, CellVersion other)
        {
            return instance.Equals(other);
        }

        public static bool operator !=(CellVersion instance, CellVersion other)
        {
            return !instance.Equals(other);
        }

        public static bool operator >(CellVersion instance, CellVersion other)
        {
            return instance.CompareTo(other) == -1;
        }

        public static bool operator <(CellVersion instance, CellVersion other)
        {
            return instance.CompareTo(other) == 1;
        }

        public static bool operator <=(CellVersion instance, CellVersion other)
        {
            return instance.Equals(other) || instance.CompareTo(other) == -1;
        }

        public static bool operator >=(CellVersion instance, CellVersion other)
        {
            return instance.Equals(other) || instance.CompareTo(other) == 1;
        }

        public bool Equals(CellVersion other)
        {
            return
                this.BeginTransaction == other.BeginTransaction &&
                this.EndTransaction == other.EndTransaction;
        }

        public int CompareTo(CellVersion other)
        {
            var beginDiff = this.BeginTransaction - other.BeginTransaction;
            if (beginDiff > 0)
            {
                return 1;
            }

            if (beginDiff < 0)
            {
                return -1;
            }

            return 0;
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return false;
            }

            if (obj.GetType() != typeof(CellVersion))
            {
                return false;
            }

            return this.Equals((CellVersion)obj);
        }

        public override int GetHashCode()
        {
            return this.BeginTransaction.GetHashCode() ^ this.EndTransaction.GetHashCode();
        }

        internal static unsafe CellVersion Read(byte[] buffer, int offset)
        {
            Contract.Requires(buffer != null);
            Contract.Requires(0 <= offset);
            Contract.Requires(offset + Size < buffer.Length, ExceptionText.ValueBufferTooSmall);
            
            fixed (byte* p = &buffer[offset])
            {
                return *(CellVersion*)p;
            }
        }
        
        internal unsafe void Write(byte[] buffer, int offset)
        {
            Contract.Requires(buffer != null);
            Contract.Requires(0 <= offset);
            Contract.Requires(offset + Size < buffer.Length, ExceptionText.ValueBufferTooSmall);
            
            fixed (byte* p = &buffer[offset])
            {
                *((CellVersion*)p) = this;
            }
        }
    }
}